查看原文
其他

基于 Spring Boot 与 RabbitMQ 的邮件发送:确保 100%消息成功投递

编程疏影 路条编程
2024-09-05


基于 Spring Boot 与 RabbitMQ 的邮件发送:确保 100%消息成功投递

在当今的企业级应用开发中,邮件发送是一个常见且重要的功能需求。为了确保邮件能够稳定、高效且 100%成功投递,我们可以结合 Spring Boot 和 RabbitMQ 来实现这一目标。

技术选型与框架介绍

在构建邮件发送系统时,我们精心挑选了 Spring Boot 和 RabbitMQ 这两个强大的技术框架,并充分利用它们各自的优势来实现高效、可靠的邮件投递功能。

Spring Boot 作为一款出色的快速开发框架,为我们提供了诸多便利。它基于 Spring 框架,但极大地简化了项目的配置和部署流程。凭借其内置的自动配置机制,开发者无需繁琐地处理各种基础配置,能够迅速搭建起项目的基本架构。同时,Spring Boot 对各种主流技术和组件的良好集成性,使得我们能够轻松引入所需的依赖,快速构建功能丰富的应用。

RabbitMQ 则是一款卓越的消息代理和队列服务器,在分布式系统的消息传递中发挥着关键作用。其基于先进的消息队列机制,能够有效地应对高并发场景下的消息处理需求。通过将邮件发送任务转化为消息并放入队列中,RabbitMQ 确保了任务的有序处理和可靠传递,即使在系统面临高负载或出现故障的情况下,也能保证邮件发送不丢失、不重复,从而极大地提高了邮件投递的成功率和稳定性。

将 Spring Boot 和 RabbitMQ 相结合,能够充分发挥两者的优势。Spring Boot 提供了便捷的开发环境和高效的应用架构,而 RabbitMQ 则为邮件发送任务提供了可靠的排队和处理机制,共同保障了邮件发送的高效性和 100%投递成功率。

项目依赖配置(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>mail-sender</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>MailSender</name>
<description>基于 Spring Boot 和 RabbitMQ 的邮件发送项目</description>

<properties>
<java.version>11</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

YAML 属性文件配置(application.yml)

spring:
mail:
host: smtp.example.com # 替换为您的 SMTP 服务器地址
port: 587 # 端口
username: your_email@example.com # 发件人邮箱
password: your_password # 邮箱密码
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true

rabbitmq:
host: localhost # RabbitMQ 服务器地址
port: 5672 # 端口
username: guest
password: guest

后端代码实现

邮件发送服务类(MailService.java)

import lombok.RequiredArgsConstructor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;

@EnableScheduling
@Service
@RequiredArgsConstructor
public class MailService {

private final JavaMailSender mailSender;
private final RabbitTemplate rabbitTemplate;
private static final int MAX_RETRY = 3; // 最大重试次数
private static final Log LOGGER = LogFactory.getLog(MailService.class);

private ConcurrentHashMap<String, Integer> retryMap = new ConcurrentHashMap<>();

public boolean sendMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(content);

int retryCount = 0;
boolean sentSuccessfully = false;
String key = to + "_" + subject;

while (retryCount < MAX_RETRY &&!sentSuccessfully) {
try {
mailSender.send(message);
sentSuccessfully = true;
retryMap.remove(key);
} catch (MailException e) {
retryCount++;
LOGGER.error("发送邮件失败,重试次数: " + retryCount + ",收件人: " + to + ",主题: " + subject, e);
retryMap.put(key, retryCount);
}
}

if (!sentSuccessfully) {
LOGGER.error("邮件发送最终失败,收件人: " + to + ",主题: " + subject);
}

return sentSuccessfully;
}

@Scheduled(fixedDelay = 60000) // 每分钟执行一次
public void retryFailedMails() {
for (String key : retryMap.keySet()) {
String[] parts = key.split("_");
String to = parts[0];
String subject = parts[1];

SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);

int retryCount = retryMap.get(key);
boolean sentSuccessfully = false;

while (retryCount < MAX_RETRY &&!sentSuccessfully) {
try {
mailSender.send(message);
sentSuccessfully = true;
retryMap.remove(key);
} catch (MailException e) {
retryCount++;
if (!sentSuccessfully) {
rabbitTemplate.convertAndSend("retryQueue", key);
LOGGER.error("重试邮件发送失败,已发送到队列,收件人: " + to + ",主题: " + subject);
}
}
}

if (!sentSuccessfully) {
LOGGER.error("重试邮件发送最终失败,收件人: " + to + ",主题: " + subject);
}
}
}
}

要合理设置最大重试次数,避免过度重试导致资源浪费。同时,在重试间隔上也需要根据实际情况进行合理的设计,以平衡成功率和性能。还需要对重试过程中的异常情况进行更详细的记录和监控,将重试仍不成功的邮件发送信息发送到队列,方便后续进一步处理,提高了系统的灵活性和容错性。

RabbitMQ 配置类(RabbitMQConfig.java)

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

@Bean
public Queue mailQueue() {
return new Queue("mail_queue");
}
}

RabbitMQ 消费者(MailConsumer.java)

import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class MailConsumer {

private final MailService mailService;

@RabbitListener(queues = "mail_queue")
public void consumeMailMessage(String message) {
// 解析消息,提取收件人、主题和内容等信息
String[] parts = message.split(";");
String to = parts[0];
String subject = parts[1];
String content = parts[2];
mailService.sendMail(to, subject, content);
}
}

控制器类(MailController.java)

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MailController {

private final RabbitTemplate rabbitTemplate;

public MailController(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}

@PostMapping("/sendMail")
public String sendMail(@RequestBody MailRequest request) {
// 将邮件信息转换为字符串,发送到 RabbitMQ 队列
String message = request.getTo() + ";" + request.getSubject() + ";" + request.getContent();
// 发送到队列
rabbitTemplate.convertAndSend("mail_queue", message);
return "邮件发送请求已提交";
}
}

前端页面实现

使用 Thymeleaf 模板和 JavaScript 实现前端页面:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>邮件发送</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<h2>邮件发送</h2>
<form th:action="@{/sendMail}" method="post">
<label for="to">收件人:</label><input type="text" id="to" name="to"><br>
<label for="subject">主题:</label><input type="text" id="subject" name="subject"><br>
<label for="content">内容:</label><textarea id="content" name="content"></textarea><br>
<button type="submit">发送</button>
</form>
<div id="result"></div>

<script>
$(document).ready(function() {
$("form").submit(function(event) {
event.preventDefault();
var to = $("#to").val();
var subject = $("#subject").val();
var content = $("#content").val();
$.ajax({
url: $(this).attr("action"),
type: "POST",
data: JSON.stringify({
"to": to,
"subject": subject,
"content": content
}),
contentType: "application/json",
success: function(response) {
$("#result").text(response);
},
error: function() {
$("#result").text("发送失败");
}
});
});
});
</script>
</body>
</html>

通过以上精心设计的架构和精妙绝伦的代码实现,我们成功地借助 Spring Boot 和 RabbitMQ 的强大力量,构建出一个能够确保 100%消息成功投递的邮件发送系统。在此过程中,RabbitMQ 犹如一位坚定的卫士,确保了邮件发送任务的可靠排队和精细处理;而 Spring Boot 则宛如一位智慧的导师,提供了便捷的开发环境和丰富多样的功能支持。

相信这套解决方案将为大家在未来的类似项目开发中,提供极具价值的参考和有力的帮助,助大家在技术的海洋中乘风破浪,勇往直前!


今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。



作者:路条编程(转载请获本公众号授权,并注明作者与出处)


继续滑动看下一个
路条编程
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存